package com.zhengx.blehelper;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by zhengx on 2018/10/29
 */
public class BleScanner {

    private static final String TAG = BleScanner.class.getSimpleName();

    private boolean isDebug = false;

    private Context context;

    private Handler overTimeHandler;

    private BluetoothAdapter mBluetoothAdapter;

    /*scan result*/
    private List<BluetoothDevice> mDeviceList;
    /**
     * 设备名称过滤
     */
    private List<String> nameFileter = new ArrayList<>();

    private String directConnectMac = null;

    private boolean isDirectConnect = false;
    private boolean isFoundSpecifiedDevice = false;
    /*scan timeout*/
    private long scanTimeOut = 12 * 1000;

    private boolean isScanning = false;

    private boolean isCanceledScan = false;

    private BleCallback.OnScanStateChangedCallback onScanStateChangedCallback;

    public BleScanner() {

    }

    public BleScanner with(Context context, BluetoothAdapter bluetoothAdapter) {
        this.context = context;
        this.mBluetoothAdapter = bluetoothAdapter;
        mDeviceList = new ArrayList<>();
        overTimeHandler = new Handler();
        return this;
    }

    public BleScanner log(boolean logOpen) {
        isDebug = logOpen;
        return this;
    }

    public BleScanner timeOut(long scanTimeOut) {
        this.scanTimeOut = scanTimeOut;

        if (scanTimeOut < 0) {
            throw new IllegalArgumentException("超时时间不能设置为负数");
        }
        return this;
    }

    public BleScanner withNameFilter(List<String> nameFilter) {
        this.nameFileter = nameFilter;
        return this;
    }

    public BleScanner setScanCallback(BleCallback.OnScanStateChangedCallback scanCallback) {
        this.onScanStateChangedCallback = scanCallback;
        return this;
    }

    public boolean scan(String mac) {

        if (isScanning) {
            return false;
        }
        if (mac != null) {
            isDirectConnect = true;
            isFoundSpecifiedDevice = false;
            this.directConnectMac = mac;
        } else {
            isDirectConnect = false;
            isFoundSpecifiedDevice = false;
            this.directConnectMac = null;
        }

        registBroadcast();
        if (isDebug) {
            Log.e(TAG,"start scan");
        }

        if (this.mBluetoothAdapter == null) {

            throw new NullPointerException();
        }

        mDeviceList = new ArrayList<>();
        isScanning = mBluetoothAdapter.startDiscovery();

        isCanceledScan = false;

        return isScanning;
    }

    public boolean scan() {
        return scan(null);
    }

    public void cancelScan() {
        if (isScanning) {
            isCanceledScan = true;
            mBluetoothAdapter.cancelDiscovery();
        }
    }

    /**
     * 蓝牙扫描回调
     */
    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action == null) {
                return;
            }
            switch (action) {
                case BluetoothDevice.ACTION_FOUND:

                    BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);  //这个就是所获得的蓝牙设备。
                    if (isDebug) {
                        Log.e(TAG, "Thread info:" + Thread.currentThread().getName() +"\n" +
                                "find device: address:" + device.getAddress()+"\n"
                                + "   name:" + device.getName());
                    }

                    if (device.getName() == null) {
                        return;
                    }

                    /*add new found device to list*/
                    if (nameFileter.size() > 0 ) {
                        boolean isCorrectDevice = false;
                        for (String s : nameFileter) {
                            if (device.getName().equals(s)) {
                                isCorrectDevice = true;
                                break;
                            }
                        }
                        //名字过滤
                        if (isCorrectDevice) {
                            foundDevice(device);
                        }
                    } else {
                        //不进行名字过滤
                        foundDevice(device);
                    }

                    break;

                case BluetoothAdapter.ACTION_DISCOVERY_STARTED:

                    mDeviceList = new ArrayList<>();
                    overTimeHandler.postDelayed(scanOverTimeRunnable, scanTimeOut);

                    if (isDebug) {
                        Log.e(TAG, "Thread info:" + Thread.currentThread().getName() +"\n" +
                                "scan device started");
                    }

                    if (onScanStateChangedCallback != null) {
                        onScanStateChangedCallback.onStartScan();
                    }
                    break;
                case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
                    unRegistBroadcast();

                    isScanning = false;
                    if (isDebug) {
                        Log.e(TAG, "Thread info:" + Thread.currentThread().getName() +"\n" +
                                "scan device finished");
                    }

                    overTimeHandler.removeCallbacks(scanOverTimeRunnable);

                    if (isDirectConnect) {
                        if (!isFoundSpecifiedDevice) {
                            if (onScanStateChangedCallback != null) {
                                onScanStateChangedCallback.onFindSpecifiedDevice(null);
                            }
                        }
                    }
                    if (isCanceledScan) {
                        if (onScanStateChangedCallback != null) {
                            onScanStateChangedCallback.onFinished(isCanceledScan, mDeviceList);
                        }
                    } else {
                        if (onScanStateChangedCallback != null) {
                            onScanStateChangedCallback.onFinished(isCanceledScan, mDeviceList);
                        }
                    }

                    onScanStateChangedCallback = null;

                    break;
            }
        }
    };

    private void foundDevice(BluetoothDevice device) {

        boolean isHave = false;
        for (BluetoothDevice bluetoothDevice : mDeviceList) {
            if (bluetoothDevice.getAddress().equals(device.getAddress())) {
                isHave = true;
                break;
            }
        }
        //重复过滤
        if (!isHave) {
            mDeviceList.add(device);
            if (!isDirectConnect && onScanStateChangedCallback != null) {
                onScanStateChangedCallback.onDiscoveryDevice(device);
            }
        }

        /*if direct scanAndConnect flag is true, cancelDiscovery and scanAndConnect to the device*/
        if (isDirectConnect && device.getAddress().equals(directConnectMac)) {
            isFoundSpecifiedDevice = true;
            if (isDebug) {
                Log.e(TAG, "start 2 scanAndConnect remote :" + directConnectMac+ "\n");
            }
            if (onScanStateChangedCallback != null) {
                onScanStateChangedCallback.onFindSpecifiedDevice(device);
            }
            overTimeHandler.removeCallbacks(scanOverTimeRunnable);
        }
    }

    private Runnable scanOverTimeRunnable = new Runnable() {
        @Override
        public void run() {

            if (mBluetoothAdapter.isDiscovering()) {
                mBluetoothAdapter.cancelDiscovery();
                if (onScanStateChangedCallback != null) {
                    onScanStateChangedCallback.onTimeOut();
                }
            }
        }
    };

    private void registBroadcast() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
        intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
        intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
        context.registerReceiver(broadcastReceiver, intentFilter);
    }

    private void unRegistBroadcast() {
        context.unregisterReceiver(broadcastReceiver);
    }

    public boolean isScanning() {
        return isScanning;
    }

}
